/*
 * This program (in conjunction to send_anycast) illustrates the use of 
 * anycast triggers to lower the overhead of inserting multiple
 * triggers in i3. 
 * Usage:
 *  > recv_anycast <cfg_file.xml>
 *  > send_anycast <cfg_file.xml> <id1> <id2> <id3>
 *  (id1, id2 and id3 represents the IDs inserted by the receiver;
 *   these IDs are printed out by recv_anycast)
 *
 * In particular, in this example, instead of inserting three triggers
 * (id1, R), (id2, R), (id3, R) in i3, the receiver choses id1, id2,
 * and id3 to share the preffix with a 4th trigger (ida, R), and then
 * inserts only (ida, R) into i3; the other three triggers are inserted
 * only locally  
 */ 

#include <stdio.h>

#include "i3.h"
#include "i3_client_api.h"
#include "utils.h"

/* callbacks */
void receive_payload(cl_trigger *t, void *data, void *fun_ctx)
{
    cl_buf *clb = (cl_buf*) data;
  if (clb->data_len) {
    printf("receiving on ID: ");
    print_id(&t->t->id);
    printf("  %s\n", clb->data);
  }
}

void constraint_failed(cl_trigger *t, void *data, void *fun_ctx)
{
  printf("Trigger constraint failed: ");
  print_id(&t->t->id);
}

void trigger_inserted(cl_trigger *t, void *data, void *fun_ctx)
{
  printf("Trigger inserted:");
  print_id(&t->t->id);
}

void trigger_failure(cl_trigger *t, void *data, void *fun_ctx)
{
  printf("Trigger failed: ");
  print_id(&t->t->id);
  /* reinsert trigger */
  cl_insert_trigger(t, 0);
}

int i3_main_loop(char *cfg_file)
{
  cl_trigger *ta, *t1, *t2, *t3;
  ID  ida, id1, id2, id3;
  Key key;

  /* initialize context */
  cl_init(cfg_file);

  /* create an anycast public trigger (i.e., the trigger is l-constrained) */
  init_id_fromstr(&ida, "this is an anycast trigger");

  /* initialize the IDs of the anycast troggers; they differ only 
   * in the last octet
   */
  init_id_fromstr(&id1, "this is an anycast trigger");
  id1.x[ID_LEN-1] = 1;
  init_id_fromstr(&id2, "this is an anycast trigger");
  id2.x[ID_LEN-1] = 2;
  init_id_fromstr(&id3, "this is an anycast trigger");
  id3.x[ID_LEN-1] = 3;

  /* create triggers using a random key */
  set_random_key(&key);

  ta = cl_create_trigger(&ida, MIN_PREFIX_LEN, &key, 
			CL_TRIGGER_CFLAG_L_CONSTRAINT|CL_TRIGGER_CFLAG_PUBLIC);
  t1 = cl_create_trigger(&id1, ID_LEN_BITS, &key, 
			CL_TRIGGER_CFLAG_L_CONSTRAINT|CL_TRIGGER_CFLAG_PUBLIC);
  t2 = cl_create_trigger(&id2, ID_LEN_BITS, &key, 
			CL_TRIGGER_CFLAG_L_CONSTRAINT|CL_TRIGGER_CFLAG_PUBLIC);
  t3 = cl_create_trigger(&id3, ID_LEN_BITS, &key, 
			CL_TRIGGER_CFLAG_L_CONSTRAINT|CL_TRIGGER_CFLAG_PUBLIC);
  
  printf("Listening on the following public IDs:\n");
  print_id(&id1);
  print_id(&id2);
  print_id(&id3);

  /* insert triggers */
  cl_insert_trigger(ta, 0);
  cl_insert_trigger(t1, CL_IFLAGS_TRIGGER_LOCAL);
  cl_insert_trigger(t2, CL_IFLAGS_TRIGGER_LOCAL);
  cl_insert_trigger(t3, CL_IFLAGS_TRIGGER_LOCAL);
  
  /* associate callbacks with the inserted triggers */
  cl_register_trigger_callback(ta, CL_CBK_TRIGGER_CONSTRAINT_FAILED, 
			       constraint_failed, NULL);
  cl_register_trigger_callback(t1, CL_CBK_TRIGGER_CONSTRAINT_FAILED, 
			       constraint_failed, NULL);
  cl_register_trigger_callback(t2, CL_CBK_TRIGGER_CONSTRAINT_FAILED, 
			       constraint_failed, NULL);
  cl_register_trigger_callback(t3, CL_CBK_TRIGGER_CONSTRAINT_FAILED, 
			       constraint_failed, NULL);

  /* callbacks to be invoked once the trigger is inserted */ 
  cl_register_trigger_callback(ta, CL_CBK_TRIGGER_INSERTED, 
			       trigger_inserted, NULL);
  cl_register_trigger_callback(t1, CL_CBK_TRIGGER_INSERTED, 
			       trigger_inserted, NULL);
  cl_register_trigger_callback(t2, CL_CBK_TRIGGER_INSERTED, 
			       trigger_inserted, NULL);
  cl_register_trigger_callback(t3, CL_CBK_TRIGGER_INSERTED, 
			       trigger_inserted, NULL);

  /* callbacks to be invoked if the trigger refresh fails */
  cl_register_trigger_callback(ta, CL_CBK_TRIGGER_REFRESH_FAILED, 
			       trigger_failure, NULL);

  /* callbacks to be invoked on packet arrival */
  cl_register_trigger_callback(t1, CL_CBK_RECEIVE_PAYLOAD, 
			       receive_payload, NULL);
  cl_register_trigger_callback(t2, CL_CBK_RECEIVE_PAYLOAD, 
			       receive_payload, NULL);
  cl_register_trigger_callback(t3, CL_CBK_RECEIVE_PAYLOAD, 
			       receive_payload, NULL);

  /* loop and call cl_select */
  cl_loop();

  /* remove & destroy triggers */
  cl_destroy_trigger(ta);
  cl_destroy_trigger(t1);
  cl_destroy_trigger(t2);
  cl_destroy_trigger(t3);
  
  /* destroy context */
  cl_exit();
}


int main(int argc, char **argv)
{
  if (argc != 2) {
    printf("%s <cfg_file.xml>\n", argv[0]);
    exit(-1);
  }
  
  i3_main_loop(argv[1]);
  return -1;
}

